"""认证与授权模块"""
from datetime import datetime, timedelta
from typing import Dict, Any, Optional, List

import jwt
from fastapi import Depends, HTTPException, Security
from fastapi.security import OAuth2PasswordBearer, SecurityScopes
from pydantic import BaseModel, ValidationError

from fin_senti_entity_platform.utils.config_loader import get_config
from fin_senti_entity_platform.utils.logger import get_logger

logger = get_logger(__name__)

# 创建OAuth2密码Bearer令牌
oauth2_scheme = OAuth2PasswordBearer(
    tokenUrl="/api/v1/auth/token",
    scopes={
        "read:sentiment": "读取情感分析结果",
        "write:sentiment": "提交情感分析任务",
        "read:entity": "读取实体识别结果",
        "write:entity": "提交实体识别任务",
        "admin": "管理权限"
    }
)

class TokenData(BaseModel):
    """令牌数据模型"""
    username: Optional[str] = None
    scopes: List[str] = []

class AuthManager:
    """认证管理器"""
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super(AuthManager, cls).__new__(cls)
        return cls._instance
    
    def __init__(self):
        if not hasattr(self, '_initialized'):
            self.config = get_config()
            self.secret_key = self.config.get('security', {}).get('secret_key', 'default_secret_key')
            self.algorithm = self.config.get('security', {}).get('algorithm', 'HS256')
            self.access_token_expire_minutes = self.config.get('security', {}).get('access_token_expire_minutes', 30)
            
            # 这里简化实现，实际应用中应该从数据库或其他存储中获取用户信息
            self.fake_users_db = {
                "admin": {
                    "username": "admin",
                    "full_name": "Admin User",
                    "email": "admin@example.com",
                    "hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",  # secret
                    "disabled": False,
                    "scopes": ["read:sentiment", "write:sentiment", "read:entity", "write:entity", "admin"]
                },
                "user": {
                    "username": "user",
                    "full_name": "Normal User",
                    "email": "user@example.com",
                    "hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",  # secret
                    "disabled": False,
                    "scopes": ["read:sentiment", "read:entity"]
                }
            }
            
            self._initialized = True
    
    def verify_password(self, plain_password: str, hashed_password: str) -> bool:
        """验证密码是否正确
        
        Args:
            plain_password: 明文密码
            hashed_password: 哈希密码
            
        Returns:
            bool: 密码是否正确
        """
        # 这里简化实现，实际应用中应该使用密码哈希库如bcrypt
        return plain_password == "secret"  # 仅用于演示
    
    def get_user(self, username: str) -> Optional[Dict[str, Any]]:
        """获取用户信息
        
        Args:
            username: 用户名
            
        Returns:
            Dict: 用户信息
        """
        if username in self.fake_users_db:
            user_dict = self.fake_users_db[username]
            return user_dict
        return None
    
    def authenticate_user(self, username: str, password: str) -> Optional[Dict[str, Any]]:
        """认证用户
        
        Args:
            username: 用户名
            password: 密码
            
        Returns:
            Dict: 用户信息，如果认证失败返回None
        """
        user = self.get_user(username)
        if not user:
            return None
        if not self.verify_password(password, user.get('hashed_password')):
            return None
        if user.get('disabled'):
            return None
        return user
    
    def create_access_token(self, data: Dict[str, Any], expires_delta: Optional[timedelta] = None) -> str:
        """创建访问令牌
        
        Args:
            data: 令牌数据
            expires_delta: 过期时间差
            
        Returns:
            str: 访问令牌
        """
        to_encode = data.copy()
        if expires_delta:
            expire = datetime.utcnow() + expires_delta
        else:
            expire = datetime.utcnow() + timedelta(minutes=self.access_token_expire_minutes)
        to_encode.update({"exp": expire})
        encoded_jwt = jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
        return encoded_jwt
    
    async def get_current_user(self, security_scopes: SecurityScopes, token: str = Depends(oauth2_scheme)) -> Dict[str, Any]:
        """获取当前用户
        
        Args:
            security_scopes: 安全作用域
            token: 访问令牌
            
        Returns:
            Dict: 用户信息
        """
        if security_scopes.scopes:
            authenticate_value = f"Bearer scope=\"{security_scopes.scope_str}\""
        else:
            authenticate_value = "Bearer"
        credentials_exception = HTTPException(
            status_code=401,
            detail="无法验证凭据",
            headers={"WWW-Authenticate": authenticate_value},
        )
        try:
            payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
            username: str = payload.get("sub")
            if username is None:
                raise credentials_exception
            token_scopes = payload.get("scopes", [])
            token_data = TokenData(scopes=token_scopes, username=username)
        except (jwt.PyJWTError, ValidationError):
            raise credentials_exception
        user = self.get_user(username=token_data.username)
        if user is None:
            raise credentials_exception
        for scope in security_scopes.scopes:
            if scope not in token_data.scopes:
                raise HTTPException(
                    status_code=403,
                    detail="权限不足",
                    headers={"WWW-Authenticate": authenticate_value},
                )
        return user
    
    async def get_current_active_user(
        self, current_user: Dict[str, Any] = Security(get_current_user, scopes=[])
    ) -> Dict[str, Any]:
        """获取当前活跃用户
        
        Args:
            current_user: 当前用户信息
            
        Returns:
            Dict: 用户信息
        """
        if current_user.get('disabled'):
            raise HTTPException(status_code=400, detail="禁用用户")
        return current_user